001 /* 002 /* 003 * Copyright 2005 Stephen J. McConnell 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 014 * implied. 015 * 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020 package net.dpml.library.info; 021 022 import java.io.File; 023 import java.io.IOException; 024 import java.io.FileNotFoundException; 025 import java.net.URI; 026 import java.net.URISyntaxException; 027 import java.util.List; 028 import java.util.ArrayList; 029 import java.util.Properties; 030 031 import net.dpml.library.Feature; 032 import net.dpml.library.TypeBuilder; 033 import net.dpml.library.info.ResourceDirective.Classifier; 034 035 import net.dpml.lang.Category; 036 import net.dpml.lang.Part; 037 import net.dpml.lang.Version; 038 039 import net.dpml.util.DecodingException; 040 import net.dpml.util.DOM3DocumentBuilder; 041 import net.dpml.util.ElementHelper; 042 043 import org.w3c.dom.Element; 044 import org.w3c.dom.Document; 045 import org.w3c.dom.TypeInfo; 046 047 /** 048 * Utility class used for construction of a module model from an XML source. 049 * 050 * @author <a href="http://www.dpml.net">The Digital Product Meta Library</a> 051 * @version 1.0.0 052 */ 053 public final class LibraryDecoder extends LibraryConstants 054 { 055 private static final DOM3DocumentBuilder DOCUMENT_BUILDER = new DOM3DocumentBuilder(); 056 057 /** 058 * Construct a library directive from XML source. 059 * @param source the XML source file 060 * @return the library directive 061 * @exception IOException if an IO exception occurs 062 */ 063 public LibraryDirective build( File source ) throws IOException 064 { 065 if( null == source ) 066 { 067 throw new NullPointerException( "source" ); 068 } 069 if( !source.exists() ) 070 { 071 throw new FileNotFoundException( source.toString() ); 072 } 073 if( source.isDirectory() ) 074 { 075 final String error = 076 "File [" 077 + source 078 + "] references a directory."; 079 throw new IllegalArgumentException( error ); 080 } 081 try 082 { 083 final Element root = getRootElement( source ); 084 File base = source.getParentFile(); 085 return buildLibraryDirective( base, root ); 086 } 087 catch( Exception e ) 088 { 089 final String error = 090 "Unexpected error while attempting to load library." 091 + "File: '" + source + "'"; 092 IOException ioe = new IOException( error ); 093 ioe.initCause( e ); 094 throw ioe; 095 } 096 } 097 098 /** 099 * Construct a resource directive from source. 100 * @param uri the source uri 101 * @return the resource directive 102 * @exception IOException if an IO exception occurs 103 */ 104 public ResourceDirective buildResource( URI uri ) throws IOException 105 { 106 try 107 { 108 final Document document = DOCUMENT_BUILDER.parse( uri ); 109 Element root = document.getDocumentElement(); 110 return buildResourceDirectiveFromElement( null, root, null ); 111 } 112 catch( Exception e ) 113 { 114 final String error = 115 "Unexpected error while attempting to load module." 116 + "URI: '" + uri + "'"; 117 IOException ioe = new IOException( error ); 118 ioe.initCause( e ); 119 throw ioe; 120 } 121 } 122 123 /** 124 * Resolve the root DOM element of the supplied file. 125 * @param source the source XML file 126 * @return the root element 127 * @exception IOException if an io error occurs 128 */ 129 private Element getRootElement( File source ) throws IOException 130 { 131 File file = source.getCanonicalFile(); 132 final Document document = DOCUMENT_BUILDER.parse( file.toURI() ); 133 return document.getDocumentElement(); 134 } 135 136 /** 137 * Build a library directive using an XML element. 138 * @param base the base directory 139 * @param element the module element 140 * @return the library directive 141 * @exception Exception if an exception occurs 142 */ 143 private LibraryDirective buildLibraryDirective( File base, Element element ) throws Exception 144 { 145 final String elementName = element.getTagName(); 146 if( !LIBRARY_ELEMENT_NAME.equals( elementName ) ) 147 { 148 final String error = 149 "Element is not a library."; 150 throw new IllegalArgumentException( error ); 151 } 152 153 // get type descriptors, modules and properties 154 155 Properties properties = null; 156 ImportDirective[] imports = new ImportDirective[0]; 157 List list = new ArrayList(); 158 Element[] children = ElementHelper.getChildren( element ); 159 for( int i=0; i<children.length; i++ ) 160 { 161 Element child = children[i]; 162 final String tag = child.getTagName(); 163 if( PROPERTIES_ELEMENT_NAME.equals( tag ) ) 164 { 165 properties = buildProperties( child ); 166 } 167 else if( IMPORTS_ELEMENT_NAME.equals( tag ) ) 168 { 169 imports = buildImportDirectives( child ); 170 } 171 else 172 { 173 ResourceDirective resource = buildResourceDirectiveFromElement( base, child, null ); 174 list.add( resource ); 175 } 176 } 177 ResourceDirective[] resources = (ResourceDirective[]) list.toArray( new ResourceDirective[0] ); 178 return new LibraryDirective( imports, resources, properties ); 179 } 180 181 /** 182 * Build a resource using an XML element. 183 * @param base the base directory 184 * @param element the module element 185 * @param offset the imported file directory offset 186 * @throws Exception if an error occurs 187 */ 188 private ResourceDirective buildResourceDirectiveFromElement( 189 File base, Element element, String offset ) throws Exception 190 { 191 final String elementName = element.getTagName(); 192 final String path = ElementHelper.getAttribute( element, "file" ); 193 if( null != path ) 194 { 195 try 196 { 197 File file = new File( base, path ); 198 File source = file.getCanonicalFile(); 199 if( !source.exists() ) 200 { 201 final String error = 202 "Local file does not exist." 203 + "\n base: " + base 204 + "\n path: " + path 205 + "\n source: " + source; 206 throw new DecodingException( element, error ); 207 } 208 else if( source.isDirectory() ) 209 { 210 final String error = 211 "Local file references a directory." 212 + "\n base: " + base 213 + "\n path: " + path 214 + "\n source: " + source; 215 throw new DecodingException( element, error ); 216 } 217 else 218 { 219 final File parent = source.getParentFile(); 220 final Element root = getRootElement( source ); 221 String basedir = getRelativePath( base, parent ); 222 if( null != offset ) 223 { 224 basedir = offset + "/" + basedir; 225 } 226 return buildResourceDirectiveFromElement( base, root, basedir ); 227 } 228 } 229 catch( DecodingException e ) 230 { 231 throw e; 232 } 233 catch( Throwable e ) 234 { 235 final String error = 236 "Internal error while attempting to resolve a import directive."; 237 throw new DecodingException( element, error, e ); 238 } 239 } 240 else 241 { 242 return buildResourceDirective( base, element, offset ); 243 } 244 } 245 246 private ResourceDirective buildResourceDirective( File base, Element element ) throws Exception 247 { 248 return buildResourceDirective( base, element, null ); 249 } 250 251 private ResourceDirective buildResourceDirective( File base, Element element, String path ) throws Exception 252 { 253 Classifier classifier = null; 254 final String tag = element.getTagName(); 255 if( RESOURCE_ELEMENT_NAME.equals( tag ) || PROJECT_ELEMENT_NAME.equals( tag ) 256 || MODULE_ELEMENT_NAME.equals( tag ) ) 257 { 258 final String name = ElementHelper.getAttribute( element, "name" ); 259 final String version = ElementHelper.getAttribute( element, "version" ); 260 String basedir = ElementHelper.getAttribute( element, "basedir" ); 261 if( path != null ) 262 { 263 if( basedir == null ) 264 { 265 basedir = path; 266 } 267 else 268 { 269 basedir = path + "/" + basedir; 270 } 271 } 272 273 if( PROJECT_ELEMENT_NAME.equals( tag ) ) 274 { 275 classifier = Classifier.LOCAL; 276 if( null == basedir ) 277 { 278 basedir = "."; 279 } 280 } 281 else if( MODULE_ELEMENT_NAME.equals( tag ) ) 282 { 283 if( null != basedir ) 284 { 285 classifier = Classifier.LOCAL; 286 } 287 else 288 { 289 classifier = Classifier.EXTERNAL; 290 } 291 } 292 else 293 { 294 classifier = Classifier.EXTERNAL; 295 } 296 297 final InfoDirective info = 298 buildInfoDirective( 299 ElementHelper.getChild( element, "info" ) ); 300 301 final DataDirective[] data = 302 buildDataTypes( 303 ElementHelper.getChild( element, "types" ) ); 304 305 final DependencyDirective[] dependencies = 306 buildDependencyDirectives( 307 ElementHelper.getChild( element, "dependencies" ) ); 308 309 final FilterDirective[] filters = 310 buildFilterDirectives( 311 ElementHelper.getChild( element, "filters" ) ); 312 313 final Properties properties = 314 buildProperties( 315 ElementHelper.getChild( element, "properties" ) ); 316 317 if( MODULE_ELEMENT_NAME.equals( tag ) ) 318 { 319 File anchor = getAnchorDirectory( base, basedir ); 320 ArrayList list = new ArrayList(); 321 Element[] children = ElementHelper.getChildren( element ); 322 for( int i=0; i<children.length; i++ ) 323 { 324 Element child = children[i]; 325 final String t = child.getTagName(); 326 327 if( RESOURCE_ELEMENT_NAME.equals( t ) 328 || PROJECT_ELEMENT_NAME.equals( t ) 329 || MODULE_ELEMENT_NAME.equals( t ) ) 330 { 331 ResourceDirective directive = 332 buildResourceDirectiveFromElement( anchor, child, null ); 333 list.add( directive ); 334 } 335 } 336 337 ResourceDirective[] resources = 338 (ResourceDirective[]) list.toArray( new ResourceDirective[0] ); 339 return ModuleDirective.createModuleDirective( 340 name, version, classifier, basedir, info, data, dependencies, 341 properties, filters, resources ); 342 } 343 else 344 { 345 return ResourceDirective.createResourceDirective( 346 name, version, classifier, basedir, info, data, dependencies, 347 properties, filters ); 348 } 349 } 350 else 351 { 352 final String error = 353 "Invalid element name [" 354 + tag 355 + "]."; 356 throw new DecodingException( element, error ); 357 } 358 } 359 360 private File getAnchorDirectory( File base, String path ) throws IOException 361 { 362 if( null == base ) 363 { 364 return null; 365 } 366 if( !base.exists() ) 367 { 368 final String error = 369 "Base directory [" + base + "] does not exist."; 370 throw new IllegalArgumentException( error ); 371 } 372 if( null == path ) 373 { 374 return base; 375 } 376 else 377 { 378 return new File( base, path ).getCanonicalFile(); 379 } 380 } 381 382 private String getRelativePath( File base, File dir ) throws IOException 383 { 384 String baseSpec = base.getCanonicalPath(); 385 String dirSpec = dir.getCanonicalPath(); 386 if( dirSpec.equals( baseSpec ) ) 387 { 388 return "."; 389 } 390 if( dirSpec.startsWith( baseSpec ) ) 391 { 392 return dirSpec.substring( baseSpec.length() + 1 ); 393 } 394 else 395 { 396 final String error = 397 "Supplied dir [" + dirSpec + "] is not with base [" + baseSpec + "]."; 398 throw new IllegalArgumentException( error ); 399 } 400 } 401 402 /** 403 * Build an array of include directives contained within the supplied enclosing element. 404 * @param element the enclosing element 405 * @return the array of includes 406 */ 407 private ImportDirective[] buildImportDirectives( Element element ) throws DecodingException 408 { 409 Element[] children = ElementHelper.getChildren( element ); 410 ImportDirective[] includes = new ImportDirective[ children.length ]; 411 for( int i=0; i<children.length; i++ ) 412 { 413 Element child = children[i]; 414 includes[i] = buildImportDirective( child ); 415 } 416 return includes; 417 } 418 419 private ImportDirective buildImportDirective( Element element ) throws DecodingException 420 { 421 final String tag = element.getTagName(); 422 final Properties properties = buildProperties( element ); 423 if( IMPORT_ELEMENT_NAME.equals( tag ) ) 424 { 425 try 426 { 427 if( element.hasAttribute( "file" ) ) 428 { 429 final String value = ElementHelper.getAttribute( element, "file", null ); 430 return new ImportDirective( ImportDirective.FILE, value, properties ); 431 } 432 else if( element.hasAttribute( "uri" ) ) 433 { 434 final String value = ElementHelper.getAttribute( element, "uri", null ); 435 return new ImportDirective( ImportDirective.URI, value, properties ); 436 } 437 else 438 { 439 final String error = 440 "Import element does not declare a 'file' or 'uri' attribute.\n" 441 + element.toString(); 442 throw new IllegalArgumentException( error ); 443 } 444 } 445 catch( Throwable e ) 446 { 447 final String error = 448 "Internal error while attempting to resolve an import directive."; 449 throw new DecodingException( element, error, e ); 450 } 451 } 452 else 453 { 454 final String error = 455 "Invalid include element name [" 456 + tag 457 + "]."; 458 throw new IllegalArgumentException( error ); 459 } 460 } 461 462 private DependencyDirective[] buildDependencyDirectives( Element element ) throws DecodingException 463 { 464 if( null == element ) 465 { 466 return new DependencyDirective[0]; 467 } 468 else 469 { 470 final String tag = element.getTagName(); 471 if( DEPENDENCIES_ELEMENT_NAME.equals( tag ) ) 472 { 473 Element[] children = ElementHelper.getChildren( element ); 474 DependencyDirective[] dependencies = new DependencyDirective[ children.length ]; 475 for( int i=0; i<children.length; i++ ) 476 { 477 Element child = children[i]; 478 dependencies[i] = buildDependencyDirective( child ); 479 } 480 return dependencies; 481 } 482 else 483 { 484 final String error = 485 "Invalid dependency element name [" 486 + tag 487 + "]."; 488 throw new IllegalArgumentException( error ); 489 } 490 } 491 } 492 493 private DependencyDirective buildDependencyDirective( Element element ) throws DecodingException 494 { 495 String name = element.getTagName(); 496 Scope scope = Scope.parse( name ); 497 if( Scope.BUILD.equals( scope ) ) 498 { 499 IncludeDirective[] includes = buildIncludeDirectives( element, false ); 500 return new DependencyDirective( Scope.BUILD, includes ); 501 } 502 else if( Scope.RUNTIME.equals( scope ) ) 503 { 504 IncludeDirective[] includes = buildIncludeDirectives( element, true ); 505 return new DependencyDirective( Scope.RUNTIME, includes ); 506 } 507 else 508 { 509 IncludeDirective[] includes = buildIncludeDirectives( element, false ); 510 return new DependencyDirective( Scope.TEST, includes ); 511 } 512 } 513 514 /** 515 * Build an array of include directives contained within the supplied enclosing element. 516 * @param element the enclosing element 517 * @param flag if category processing is required 518 * @return the array of includes 519 */ 520 private IncludeDirective[] buildIncludeDirectives( Element element, boolean flag ) throws DecodingException 521 { 522 Element[] children = ElementHelper.getChildren( element ); 523 IncludeDirective[] includes = new IncludeDirective[ children.length ]; 524 for( int i=0; i<children.length; i++ ) 525 { 526 Element child = children[i]; 527 includes[i] = buildIncludeDirective( child, flag ); 528 } 529 return includes; 530 } 531 532 private IncludeDirective buildIncludeDirective( Element element, boolean flag ) throws DecodingException 533 { 534 final String tag = element.getTagName(); 535 final Properties properties = buildProperties( element ); 536 if( INCLUDE_ELEMENT_NAME.equals( tag ) ) 537 { 538 Category category = buildCategory( element, flag ); 539 if( element.hasAttribute( "key" ) ) 540 { 541 final String value = ElementHelper.getAttribute( element, "key", null ); 542 return new IncludeDirective( IncludeDirective.KEY, category, value, properties ); 543 } 544 else if( element.hasAttribute( "ref" ) ) 545 { 546 final String value = ElementHelper.getAttribute( element, "ref", null ); 547 return new IncludeDirective( IncludeDirective.REF, category, value, properties ); 548 } 549 else if( element.hasAttribute( "uri" ) ) 550 { 551 final String value = ElementHelper.getAttribute( element, "uri", null ); 552 return new IncludeDirective( IncludeDirective.URI, category, value, properties ); 553 } 554 else 555 { 556 final String error = 557 "Include directive does not declare a 'uri', 'key' or 'ref' attribute.\n" 558 + element.toString(); 559 throw new IllegalArgumentException( error ); 560 } 561 } 562 else 563 { 564 final String error = 565 "Invalid include element name [" 566 + tag 567 + "]."; 568 throw new DecodingException( element, error ); 569 } 570 } 571 572 private Category buildCategory( Element element, boolean flag ) 573 { 574 if( !flag ) 575 { 576 return Category.UNDEFINED; 577 } 578 else 579 { 580 final String value = ElementHelper.getAttribute( element, "tag", "private" ); 581 return Category.parse( value ); 582 } 583 } 584 585 private InfoDirective buildInfoDirective( Element element ) 586 { 587 if( null == element ) 588 { 589 return null; 590 } 591 else 592 { 593 String title = ElementHelper.getAttribute( element, "title" ); 594 Element child = ElementHelper.getChild( element, "description" ); 595 if( null == child ) 596 { 597 return new InfoDirective( title, null ); 598 } 599 else 600 { 601 String value = ElementHelper.getValue( child ); 602 String description = trim( value ); 603 return new InfoDirective( title, description ); 604 } 605 } 606 } 607 608 private String trim( String value ) 609 { 610 if( null == value ) 611 { 612 return null; 613 } 614 String trimmed = value.trim(); 615 if( trimmed.startsWith( "\n" ) ) 616 { 617 return trim( trimmed.substring( 1 ) ); 618 } 619 else if( trimmed.endsWith( "\n" ) ) 620 { 621 return trim( trimmed.substring( 0, trimmed.length() - 1 ) ); 622 } 623 else 624 { 625 return trimmed; 626 } 627 } 628 629 private FilterDirective[] buildFilterDirectives( Element element ) throws Exception 630 { 631 if( null == element ) 632 { 633 return new FilterDirective[0]; 634 } 635 else 636 { 637 Element[] children = ElementHelper.getChildren( element ); 638 FilterDirective[] filters = new FilterDirective[ children.length ]; 639 for( int i=0; i<children.length; i++ ) 640 { 641 Element child = children[i]; 642 String token = ElementHelper.getAttribute( child, "token" ); 643 if( "filter".equals( child.getTagName() ) ) 644 { 645 String value = ElementHelper.getAttribute( child, "value" ); 646 filters[i] = new SimpleFilterDirective( token, value ); 647 } 648 else if( "feature".equals( child.getTagName() ) ) 649 { 650 String id = ElementHelper.getAttribute( child, "id" ); 651 Feature feature = Feature.parse( id ); 652 String ref = ElementHelper.getAttribute( child, "ref" ); 653 String type = ElementHelper.getAttribute( child, "type" ); 654 boolean alias = ElementHelper.getBooleanAttribute( child, "alias" ); 655 filters[i] = new FeatureFilterDirective( token, ref, feature, type, alias ); 656 } 657 else 658 { 659 final String error = 660 "Element name not recognized [" 661 + child.getTagName() 662 + "] (expecting 'filter')."; 663 throw new DecodingException( element, error ); 664 } 665 } 666 return filters; 667 } 668 } 669 670 private DataDirective[] buildDataTypes( Element element ) throws Exception 671 { 672 if( null == element ) 673 { 674 return new DataDirective[0]; 675 } 676 Element[] children = ElementHelper.getChildren( element ); 677 DataDirective[] data = new DataDirective[ children.length ]; 678 for( int i=0; i<children.length; i++ ) 679 { 680 Element child = children[i]; 681 data[i] = buildDataType( child ); 682 } 683 return data; 684 } 685 686 private DataDirective buildDataType( Element element ) throws Exception 687 { 688 TypeInfo info = element.getSchemaTypeInfo(); 689 String namespace = info.getTypeNamespace(); 690 String typeName = info.getTypeName(); 691 692 if( null == namespace ) 693 { 694 throw new NullPointerException( "namespace" ); 695 } 696 697 if( MODULE_XSD_URI.equals( namespace ) ) 698 { 699 // it's a generic type declaration 700 701 final String id = getID( element ); 702 final Properties properties = getProperties( element ); 703 final Version version = getVersion( element ); 704 return new TypeDirective( id, version, properties ); 705 } 706 else if( info.isDerivedFrom( MODULE_XSD_URI, "AbstractType", TypeInfo.DERIVATION_EXTENSION ) ) 707 { 708 if( 709 PART_XSD_URI.equals( namespace ) 710 || info.isDerivedFrom( PART_XSD_URI, "StrategyType", TypeInfo.DERIVATION_EXTENSION ) ) 711 { 712 final Version version = getVersion( element ); 713 return new TypeDirective( element, "part", version ); 714 } 715 else 716 { 717 // id attribute is required 718 719 final String id = getID( element ); 720 final Version version = getVersion( element ); 721 return new TypeDirective( element, id, version ); 722 } 723 } 724 else 725 { 726 final String error = 727 "Unsupported element encountered during type directive production." 728 + "\nNamespace: " 729 + namespace 730 + "\nType: " 731 + info.getTypeName() 732 + "\nName: " 733 + element.getTagName(); 734 throw new DecodingException( element, error ); 735 } 736 } 737 738 private Version getVersion( Element element ) 739 { 740 final String version = ElementHelper.getAttribute( element, "version" ); 741 if( null != version ) 742 { 743 return Version.parse( version ); 744 } 745 else if( getAliasFlag( element ) ) 746 { 747 return Version.NULL_VERSION; 748 } 749 else 750 { 751 return null; 752 } 753 } 754 755 private Properties buildProperties( Element element ) 756 { 757 if( null == element ) 758 { 759 return null; 760 } 761 Properties properties = new Properties(); 762 Element[] children = ElementHelper.getChildren( element ); 763 for( int i=0; i<children.length; i++ ) 764 { 765 Element child = children[i]; 766 String tag = child.getTagName(); 767 if( "property".equals( tag ) ) 768 { 769 String key = ElementHelper.getAttribute( child, "name", null ); 770 if( null == key ) 771 { 772 final String error = 773 "Property declaration does not contain a 'name' attribute."; 774 throw new IllegalArgumentException( error ); 775 } 776 else 777 { 778 String value = ElementHelper.getAttribute( child, "value", null ); 779 properties.setProperty( key, value ); 780 } 781 } 782 } 783 return properties; 784 } 785 786 private URI getURIFromSpec( String spec ) 787 { 788 if( null == spec ) 789 { 790 return null; 791 } 792 else 793 { 794 try 795 { 796 return new URI( spec ); 797 } 798 catch( URISyntaxException e ) 799 { 800 final String error = 801 "Type descriptor uri [" 802 + spec 803 + "] could not be converted to a URI value."; 804 throw new IllegalArgumentException( error ); 805 } 806 } 807 } 808 809 private TypeBuilder loadTypeBuilder( URI uri ) throws Exception 810 { 811 ClassLoader classloader = TypeBuilder.class.getClassLoader(); 812 Object[] args = new Object[0]; 813 Part part = Part.load( uri ); 814 Object handler = part.instantiate( args ); 815 if( handler instanceof TypeBuilder ) 816 { 817 return (TypeBuilder) handler; 818 } 819 else 820 { 821 final String error = 822 "Plugin [" 823 + uri 824 + "] does not implement the " 825 + TypeBuilder.class.getName() 826 + " interface."; 827 throw new IllegalArgumentException( error ); 828 } 829 } 830 831 /** 832 * Return the id attribute of the supplied element. 833 * @param element the DOM element 834 * @return the id value 835 * @exception DecodingException if an error occurs during element evaluation 836 */ 837 protected String getID( Element element ) throws DecodingException 838 { 839 final String id = ElementHelper.getAttribute( element, "id" ); 840 if( null == id ) 841 { 842 final String error = 843 "Missing type 'id'."; 844 throw new DecodingException( element, error ); 845 } 846 else 847 { 848 return id; 849 } 850 } 851 852 /** 853 * Return the alias attribute of the supplied element. 854 * @param element the DOM element 855 * @return the alias production flag value 856 */ 857 protected boolean getAliasFlag( Element element ) 858 { 859 return ElementHelper.getBooleanAttribute( element, "alias", false ); 860 } 861 862 /** 863 * Return properties declared within the supplied element as immediate 864 * child <property> elements. 865 * @param element the enclosing DOM element 866 * @return the resolved properties instance 867 */ 868 protected Properties getProperties( Element element ) 869 { 870 Properties properties = new Properties(); 871 Element[] children = ElementHelper.getChildren( element ); 872 for( int i=0; i<children.length; i++ ) 873 { 874 Element child = children[i]; 875 String tag = child.getTagName(); 876 if( "property".equals( tag ) ) 877 { 878 String key = ElementHelper.getAttribute( child, "name", null ); 879 if( null == key ) 880 { 881 final String error = 882 "Property declaration does not contain a 'name' attribute."; 883 throw new IllegalArgumentException( error ); 884 } 885 else 886 { 887 String value = ElementHelper.getAttribute( child, "value", null ); 888 properties.setProperty( key, value ); 889 } 890 } 891 } 892 return properties; 893 } 894 }